package com.xxchat.service.impl;

import java.io.File;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import javax.annotation.Resource;

import com.xxchat.config.AppConfig;
import com.xxchat.entity.constants.SysConstants;
import com.xxchat.entity.dto.MessageSendDTO;
import com.xxchat.entity.dto.SysSettingDTO;
import com.xxchat.entity.dto.TokenUserInfoDTO;
import com.xxchat.entity.enums.*;
import com.xxchat.entity.po.ChatSession;
import com.xxchat.entity.po.ChatSessionUser;
import com.xxchat.entity.po.UserContact;
import com.xxchat.entity.query.*;
import com.xxchat.exception.BusinessException;
import com.xxchat.mappers.ChatSessionMapper;
import com.xxchat.mappers.ChatSessionUserMapper;
import com.xxchat.mappers.UserContactMapper;
import com.xxchat.redis.RedisComponent;
import com.xxchat.utils.CopyUtils;
import com.xxchat.utils.DateUtil;
import com.xxchat.websocket.MessageHandler;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.xxchat.entity.po.ChatMessage;
import com.xxchat.entity.vo.PaginationResultVO;
import com.xxchat.mappers.ChatMessageMapper;
import com.xxchat.service.ChatMessageService;
import com.xxchat.utils.StringTools;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.HtmlUtils;


/**
 * 聊天消息表 业务接口实现
 */
@Service("chatMessageService")
public class ChatMessageServiceImpl implements ChatMessageService {

    private static final Logger log = LoggerFactory.getLogger(ChatMessageServiceImpl.class);

    @Resource
    private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;

    @Resource
    private ChatSessionMapper<ChatSession, ChatSessionQuery> chatSessionMapper;

    @Resource
    private UserContactMapper<UserContact, UserContactQuery> userContactMapper;

    @Resource
    private RedisComponent redisComponent;

    @Resource
    private MessageHandler messageHandler;

    @Resource
    private AppConfig appConfig;

    /**
     * 根据条件查询列表
     */
    @Override
    public List<ChatMessage> findListByParam(ChatMessageQuery param) {
        return this.chatMessageMapper.selectList(param);
    }

    /**
     * 根据条件查询列表
     */
    @Override
    public Integer findCountByParam(ChatMessageQuery param) {
        return this.chatMessageMapper.selectCount(param);
    }

    /**
     * 分页查询方法
     */
    @Override
    public PaginationResultVO<ChatMessage> findListByPage(ChatMessageQuery param) {
        int count = this.findCountByParam(param);
        int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

        SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
        param.setSimplePage(page);
        List<ChatMessage> list = this.findListByParam(param);
        PaginationResultVO<ChatMessage> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
        return result;
    }

    /**
     * 新增
     */
    @Override
    public Integer add(ChatMessage bean) {
        return this.chatMessageMapper.insert(bean);
    }

    /**
     * 批量新增
     */
    @Override
    public Integer addBatch(List<ChatMessage> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.chatMessageMapper.insertBatch(listBean);
    }

    /**
     * 批量新增或者修改
     */
    @Override
    public Integer addOrUpdateBatch(List<ChatMessage> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.chatMessageMapper.insertOrUpdateBatch(listBean);
    }

    /**
     * 多条件更新
     */
    @Override
    public Integer updateByParam(ChatMessage bean, ChatMessageQuery param) {
        StringTools.checkParam(param);
        return this.chatMessageMapper.updateByParam(bean, param);
    }

    /**
     * 多条件删除
     */
    @Override
    public Integer deleteByParam(ChatMessageQuery param) {
        StringTools.checkParam(param);
        return this.chatMessageMapper.deleteByParam(param);
    }

    /**
     * 根据MessageId获取对象
     */
    @Override
    public ChatMessage getChatMessageByMessageId(Long messageId) {
        return this.chatMessageMapper.selectByMessageId(messageId);
    }

    /**
     * 根据MessageId修改
     */
    @Override
    public Integer updateChatMessageByMessageId(ChatMessage bean, Long messageId) {
        return this.chatMessageMapper.updateByMessageId(bean, messageId);
    }

    /**
     * 根据MessageId删除
     */
    @Override
    public Integer deleteChatMessageByMessageId(Long messageId) {
        return this.chatMessageMapper.deleteByMessageId(messageId);
    }

    @Override
    public List<ChatMessage> getMyMessageList(List<String> groupIds, Date lastOffTime) {
        return this.chatMessageMapper.selectMyMessageList(groupIds, lastOffTime);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MessageSendDTO saveMessage(ChatMessage chatMessage, TokenUserInfoDTO tokenUserInfoDTO) {
        // 不是机器人
        if (!SysConstants.ROBOT_UID.equals(chatMessage.getContactId())) {
            List<String> contactList = redisComponent.getUserContactList(tokenUserInfoDTO.getUserId());
            if (null == contactList) {
                throw new BusinessException(ResponseCodeEnum.CODE_500);
            }
            if (!contactList.contains(chatMessage.getContactId())) {
                UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(chatMessage.getContactId());
                if (UserContactTypeEnum.USER.equals(contactTypeEnum)) {
                    throw new BusinessException(ResponseCodeEnum.CODE_702);
                } else {
                    throw new BusinessException(ResponseCodeEnum.CODE_703);
                }
            }
        }

        String sessionId = null;
        String sendUserId = tokenUserInfoDTO.getUserId();
        String contactId = chatMessage.getContactId();
        UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(contactId);
        if (null == contactTypeEnum) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (UserContactTypeEnum.USER.equals(contactTypeEnum)) {
            sessionId = StringTools.getChatSessionId4User(new String[]{contactId, sendUserId});
        } else {
            sessionId = StringTools.getChatSessionId4Group(contactId);
        }
        chatMessage.setSessionId(sessionId);

        chatMessage.setSendTime(System.currentTimeMillis());

        MessageTypeEnum type = MessageTypeEnum.getByType(chatMessage.getMessageType());
        if (null == type || (!MessageTypeEnum.CHAT.equals(type) && !MessageTypeEnum.MEDIA_CHAT.equals(type))) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        Integer status = MessageTypeEnum.MEDIA_CHAT.equals(type) ? MessageStatusEnum.SENDING.getStatus() : MessageStatusEnum.SENDED.getStatus();
        chatMessage.setStatus(status);

        String content = HtmlUtils.htmlEscape(chatMessage.getMessageContent());
        chatMessage.setMessageContent(content);

        // 更新会话
        ChatSession chatSession = new ChatSession();
        chatSession.setSessionId(sessionId);
        if (UserContactTypeEnum.GROUP.equals(contactTypeEnum)) {
            chatSession.setLastMessage(tokenUserInfoDTO.getNickName() + ": " + content);
        }
        chatSession.setLastReceiveTime(System.currentTimeMillis());
        this.chatSessionMapper.updateBySessionId(chatSession, sessionId);

        // 记录消息表
        chatMessage.setSendUserId(sendUserId);
        chatMessage.setSendUserNickName(tokenUserInfoDTO.getNickName());
        chatMessage.setContactType(contactTypeEnum.getType());
        this.chatMessageMapper.insert(chatMessage);

        MessageSendDTO messageSendDTO = CopyUtils.copy(chatMessage, MessageSendDTO.class);

        // 机器人
        if (SysConstants.ROBOT_UID.equals(contactId)) {
            SysSettingDTO sysSetting = redisComponent.getSysSetting();
            TokenUserInfoDTO robot = new TokenUserInfoDTO();
            robot.setUserId(sysSetting.getRobotUid());
            robot.setNickName(sysSetting.getRobotNickName());

            ChatMessage message = new ChatMessage();
            message.setContactId(sendUserId);
            message.setMessageContent(sysSetting.getRobotWelcome());
            message.setMessageType(MessageTypeEnum.CHAT.getType());
            this.saveMessage(message, robot);
        } else {
            messageHandler.sendMessage(messageSendDTO);
        }

        return messageSendDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMessageFile(String userId, Long messageId, MultipartFile file, MultipartFile cover) {
        ChatMessage chatMessage = this.chatMessageMapper.selectByMessageId(messageId);
        if (null == chatMessage ||
                !chatMessage.getSendUserId().equals(userId) ||
                !MessageStatusEnum.SENDING.getStatus().equals(chatMessage.getStatus()) ||
                !MessageTypeEnum.MEDIA_CHAT.getType().equals(chatMessage.getMessageType())) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        SysSettingDTO sysSetting = redisComponent.getSysSetting();
        String fileSuffix = StringTools.getFileSuffix(Objects.requireNonNull(file.getOriginalFilename()));
        if (StringTools.isEmpty(fileSuffix)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (ArrayUtils.contains(SysConstants.IMAGE_SUFFIX_LIST, fileSuffix.toLowerCase())) {
            if (file.getSize() > sysSetting.getMaxImageSize() * SysConstants.FILE_SIZE_MB) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        } else if (ArrayUtils.contains(SysConstants.VIDEO_SUFFIX_LIST, fileSuffix.toLowerCase())) {
            if (file.getSize() > sysSetting.getMaxVideoSize() * SysConstants.FILE_SIZE_MB) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        } else {
            if (file.getSize() > sysSetting.getMaxFileSize()) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }
        String filename = file.getOriginalFilename();
        String suffix = StringTools.getFileSuffix(filename);
        String realName = messageId + suffix;
        String month = DateUtil.format(new Date(chatMessage.getSendTime()), DateTimePatternEnum.YYYY_MM_DD.getPattern());
        File folder = new File(appConfig.getProjectFolder() + SysConstants.FILE_FOLDER_PATH_FILE + File.separator + month);
        if (!folder.exists()) {
            if (!folder.mkdirs()) {
                throw new BusinessException(ResponseCodeEnum.CODE_500);
            }
        }
        File uploadFile = new File(folder.getPath() + File.separator + realName);
        try {
            file.transferTo(uploadFile);
            if (null != cover) {
                cover.transferTo(new File(folder.getPath() + File.separator + messageId + SysConstants.IMAGE_COVER_SUFFIX));
            }
        } catch (Exception e) {
            log.error("上传文件失败：{}", e.getMessage());
            throw new BusinessException(ResponseCodeEnum.CODE_504);
        }

        ChatMessage uploadInfo = new ChatMessage();
        uploadInfo.setStatus(MessageStatusEnum.SENDED.getStatus());

        ChatMessageQuery query = new ChatMessageQuery();
        query.setMessageId(messageId);
        query.setStatus(MessageStatusEnum.SENDING.getStatus());
        this.chatMessageMapper.updateByParam(uploadInfo, query);

        MessageSendDTO<Object> messageSendDTO = new MessageSendDTO<>();
        messageSendDTO.setMessageId(messageId);
        messageSendDTO.setMessageType(MessageTypeEnum.FILE_UPLOAD.getType());
        messageSendDTO.setContactId(chatMessage.getContactId());
        messageHandler.sendMessage(messageSendDTO);
    }

    @Override
    public File downloadFile(TokenUserInfoDTO userInfoDTO, String fileId, Boolean showCover) {
        ChatMessage chatMessage = chatMessageMapper.selectByMessageId(Long.valueOf(fileId));
        String contactId = chatMessage.getContactId();
        UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(contactId);
        if (UserContactTypeEnum.USER.equals(contactTypeEnum) && !userInfoDTO.getUserId().equals(contactId)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (UserContactTypeEnum.GROUP.equals(contactTypeEnum)) {
            UserContactQuery query = new UserContactQuery();
            query.setUserId(userInfoDTO.getUserId());
            query.setContactType(UserContactTypeEnum.GROUP.getType());
            query.setStatus(UserContactStatusEnum.FRIEND.getStatus());
            query.setContactId(contactId);
            Integer count = this.userContactMapper.selectCount(query);
            if (count != 1) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }

        String month = DateUtil.format(new Date(chatMessage.getSendTime()), DateTimePatternEnum.YYYY_MM_DD.getPattern());
        File folder = new File(appConfig.getProjectFolder() + SysConstants.FILE_FOLDER_PATH_FILE);
        if (!folder.exists()) {
            log.error("文件不存在：{}", folder);
            throw new BusinessException(ResponseCodeEnum.CODE_505);
        }
        String fileName = chatMessage.getFileName();
        String suffix = showCover ? SysConstants.IMAGE_COVER_SUFFIX : StringTools.getFileSuffix(fileName);
        String realName = fileId + suffix;
        File file = new File(folder.getPath() + File.separator + month + File.separator + realName);
        if (!file.exists()) {
            log.error("文件不存在：{}", realName);
            throw new BusinessException(ResponseCodeEnum.CODE_505);
        }
        return file;
    }
}